package futbol;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.logging.Level;

import futbol.gui.FutbolGUI;
import futbol.gui.FutbolGUI.GuiEvent;
import futbol.tacticas.concursantes.Tactica;
import futbol.util.Log;

public class FutbolMatch implements Runnable {
    
    public enum CompetitionMode {
      Versus,
      Tourney,
      Ligue
    };
    CompetitionMode mode = CompetitionMode.Versus;
    
    private static FutbolMatch match = null;
    
    public static final int MATCH_TIME = 1800; 
    
    public static final int STATE_LOADING = 0;
    public static final int STATE_MENU = 1;
    public static final int STATE_PLAYING = 2;
    public static final int STATE_PAUSED = 3;
    public static final int STATE_PRE_MATCH = 4;
    public static final int STATE_POS_MATCH = 5;
    
    
    
    private int actualState;
    
    private Hashtable<String, Tactica> tacticas;
    
    private FutbolGUI gui;
    
    private boolean simulate;
    private boolean logged;
    private Partido partido;

    private int time;
    
    private boolean finished;
    private boolean alive;
    
    // For tourney mode
    ArrayList<String[]> matches;
    ArrayList<String> round;
    ArrayList<String> nextRound;
    
    
    // For ligue mode
    Hashtable<String, Integer> teamPoints;
    String[] ligueTeams;
    int homeTeam;
    int visitantTeam;
    
    public static FutbolMatch instance() {
        if (match == null) {
            System.out.println("Match not created");
            System.exit(0);
        }
        return match;
    }
    
    public static FutbolMatch instance(FutbolGUI gui) throws Exception {
        if (match == null) {
            match = new FutbolMatch(gui);
        }
        return match;
    }

    private FutbolMatch(FutbolGUI gui) throws Exception {
        initTacticas();        
        initLocals();
        this.gui = gui;
    }
    
    private void initTacticas() throws Exception {
        tacticas = new Hashtable<String, Tactica>();
            
        File loadDirectory = new File(System.getProperty("user.dir")+ "/tactics/") ;
        
        if (!loadDirectory.exists() || !loadDirectory.isDirectory()) {
            throw new Exception (String.format("Directory '%s' needed", loadDirectory.getAbsolutePath()));
        }
        
        // Convert File to a URL                    
        URL url = loadDirectory.toURL();          // file:/c:/myclasses/
        URL[] urls = new URL[]{url};
    
        // Create a new class loader with the directory
        ClassLoader cl = new URLClassLoader(urls);
        
        // Recursive interation
        LinkedList<File> toIterate = new LinkedList<File>();
        toIterate.addFirst(loadDirectory);
        
        while (toIterate.size()>0) {
            File next = toIterate.remove(0);
            
            if (next.isDirectory()) {
                for (File f : next.listFiles()) {
                    toIterate.addFirst(f);
                }
            } else {
                String tactic = next.getAbsolutePath();
                tactic = tactic.replace(loadDirectory.getAbsolutePath() + File.separator, "");
                tactic = tactic.replace(".class", "");
                tactic = tactic.replace(File.separatorChar, '.');
                tactic = tactic.trim();
                
                Class<Tactica> c = (Class<Tactica>)cl.loadClass(tactic);
                Tactica t = c.getConstructor().newInstance();
                tacticas.put(t.getNombre(), t);
            }            
        }   
    }
    
    public void initLocals() {        
        simulate = false;
        partido = null;
        finished = true;
        alive = true;
        time = MATCH_TIME;
        actualState = STATE_LOADING;
    }
    
    public long getPosicionBolaParaEmpate() {
        return partido.getPosicionBolaParaEmpate();
    }

    public Hashtable<String, Tactica> getTacticas() {
        return tacticas;
    }
    public Partido getPartido() {
        return partido;
    }
    
    public int getTime() {
        return time;
    }
    
    public int getActualState() {
        return actualState;
    }
    
    public boolean getSimulate() {
        return simulate;
    }
    
    public void start() {
        new Thread(this).start();
    }
    
    public void run() {
        try {
            //Thread.sleep(6000);
            while(alive){
                switch(actualState) {
                    case STATE_LOADING:
                        // do nothing
                        break;
                    
                    case STATE_MENU:
                        gui.eventHandle(GuiEvent.GUI_EVENT_REPAINT);
                        Thread.sleep(100);
                        break;
                    case STATE_PRE_MATCH:
                        gui.eventHandle(GuiEvent.GUI_EVENT_START_MATCH);
                        setState(STATE_PLAYING);
                        finished = false;
                        break;
                    case STATE_PLAYING:                        
                        if (time<MATCH_TIME){
                            partido.mover();
                            time++;
                            if (!simulate) { 
                                Thread.sleep(100);
                            }
                        } else {
                            setState(STATE_POS_MATCH);
                            //Thread.sleep(100);
                        }
                        
                        
                        break;
                    case STATE_POS_MATCH:
                        String t1 = partido.getTactica(0).getNombre();
                        int score1 = partido.getPuntos(0);
                        
                        
                        String t2 = partido.getTactica(1).getNombre();
                        int score2 = partido.getPuntos(1);
                        
                        if (Log.isLoggable(Level.SEVERE)) {
                            Log.log(String.format("%s[0]=%d - %s[1]=%d", t1, score1, t2, score2));
                        }
                        Log.finishLog();
                        gui.eventHandle(GuiEvent.GUI_EVENT_FINISH_MATCH);   
                        
                        if (mode == CompetitionMode.Tourney) {
                            System.out.println("Wins: "  + getWinerName());
                            nextRound.add(getWinerName());
                        }
                        
                        finished = true;
                        finishMatch();
                        break;
                    case STATE_PAUSED:
                        // do nothing
                        break;                        
                }            

                gui.eventHandle(GuiEvent.GUI_EVENT_REPAINT);
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
        
    public void setState(int state) {
        this.actualState = state;
    }   
   
    
    public void startMatch(Tactica t1, Tactica t2, boolean simulate, boolean log) {
        
        Level level = (log) ? Level.ALL : Level.OFF;
        Log.startLog(t1.getNombre(), t2.getNombre(), level);
        time = 0;
        partido = new Partido(t1, t2);
        this.simulate = simulate;
        setState(STATE_PRE_MATCH);
    }
    
    public void startMatch(String t1, String t2, boolean simulate, boolean log) {        
        startMatch(tacticas.get(t1), tacticas.get(t2), simulate, log);
    }
    
    public void startVersus(String t1, String t2, boolean simulate, boolean log) {
        mode = CompetitionMode.Versus;
        startMatch(t1, t2, simulate, log);
    }
    
    public void startTourney(String[] tactics, boolean simulate, boolean log) {
        if (tactics.length > 1) {
            mode = CompetitionMode.Tourney;
            matches = new ArrayList<String[]>();
            round = new ArrayList<String>();
            for (String team : tactics) {
                round.add(team);
            }
            System.out.println("Tourney: " + round);
            nextRound = new ArrayList<String>();
            
            while (round.size()>1) {
                matches.add(new String[]{round.remove(0), round.remove(0)});
            }
            //nextRound = new ArrayList<String>();
            this.simulate = simulate;
            this.logged = log;
            
            //match.startMatch(round.remove(0), round.remove(0), simulate, logged);
            nextMatch();
        } else {
            System.out.println("At least 2 teams needed");
        }
        
    }
    
    public void startLigue(String[] tactics, boolean simulate, boolean log) {
        if (tactics.length > 1) {
            mode = CompetitionMode.Ligue;
            this.ligueTeams = tactics;
            //int teams = tactics.length;
            this.simulate = simulate;
            this.logged = log;
            homeTeam = 0;
            visitantTeam = 1;
            
            teamPoints = new Hashtable<String, Integer>();
            for (String team : tactics) {
                teamPoints.put(team, 0);
            }
            
            match.startMatch(ligueTeams[homeTeam], ligueTeams[visitantTeam], simulate, logged);
        } else {
            System.out.println("At least 2 teams needed");
        }
    }
    
    public void nextMatch() {
        if (mode == CompetitionMode.Versus) {
            setState(STATE_MENU);
        } else if (mode == CompetitionMode.Tourney) {  
            
//            if (partido != null && partido.getGanador() != -1) {
//                  System.out.println("Wins: "  + getWinerName());
//                  nextRound.add(getWinerName());
//              }
            
            if (matches.size()>0) {
                String[] nextMatch = matches.remove(0);
                System.out.println(nextMatch[0] + " vs. " + nextMatch[1]);
                match.startMatch(nextMatch[0], nextMatch[1], simulate, logged);
            } else {
                if (round.size()>0) {
                    nextRound.add(round.remove(0));
                }
                if (round.size() == 0 && nextRound.size()==1) {
                    System.out.println("Tourney finished");
                    finishCompetition();                    
                } else {
                    while (nextRound.size()>1) {
                        matches.add(new String[]{nextRound.remove(0), nextRound.remove(0)});
                    }
                    if (nextRound.size() > 0) {
                        round.add(nextRound.remove(0));
                    }
                    
                    String[] nextMatch = matches.remove(0);
                    System.out.println(nextMatch[0] + " vs. " + nextMatch[1]);
                    match.startMatch(nextMatch[0], nextMatch[1], simulate, logged);
                }
            }

            
//            
//            if (partido != null && partido.getGanador() != -1) {
//                System.out.println("Wins: "  + getWinerName());
//                nextRound.add(getWinerName());
//            }
//            
//            if (round.size()<2) {
//                if (round.size()==1 && nextRound.size() == 0) {
//                    //System.out.println(round);
//                    finishCompetition();
//                } else {
//                    if (round.size()>0) {
//                        nextRound.add(round.remove(0));
//                    }
//                    round = nextRound;
//                    nextRound = new ArrayList<String>();
//                }
//            }
//            else {
//                
//                System.out.println("tourney: " + round);
//                
//                
//                if (round.size()>=2) {                
//                    String t1 = round.remove(0);
//                    String t2 = round.remove(0);
//                    System.out.println(t1 + " vs. " + t2);
//                    match.startMatch(t1, t2, simulate, logged);
//                }
//            }
        } else if (mode == CompetitionMode.Ligue) {
            
            int winnerPoints = teamPoints.get(match.getWinerName());
            winnerPoints += 3;
            teamPoints.put(match.getWinerName(), winnerPoints);
            
            System.out.println("ligue: " + teamPoints);
            
            ++visitantTeam;
            if (visitantTeam == homeTeam) {
                ++visitantTeam;
            }
            if (visitantTeam >= ligueTeams.length) {
                visitantTeam = 0;
                ++homeTeam;
            }
            
            if (homeTeam >= ligueTeams.length) {
                System.out.println("Finished ligue");
                finishCompetition();
            } else {
                String t1 = ligueTeams[homeTeam];
                String t2 = ligueTeams[visitantTeam];
                match.startMatch(t1, t2, simulate, logged);
            }
        }
    }
    
    public void finishMatch() {        
        
        nextMatch();
    }

    public void finishCompetition() {
        setState(STATE_MENU);
    }
    public boolean finished() {
        return finished;
    }
    
    public String scoreToString() {
        return partido.scoreToString();
    }
    
    public void kill() {
        alive = false;
    }
    
    public String getWinerName() {
        
        String winner = "";
        int winnerN = partido.getGanador();
        if (winnerN != -1) {
            winner = partido.getTactica(winnerN).getNombre();
        }
        return winner;
    }

    public Tactica getTactica(String nombre) {
        return tacticas.get(nombre);
    }
}
